package com.leo.basic.scheduler.service.impl;

import com.leo.basic.scheduler.bean.OperLog;
import com.leo.basic.scheduler.bean.ResponseData;
import com.leo.basic.scheduler.bean.SchedulerJob;
import com.leo.basic.scheduler.constant.SchedulerConstant;
import com.leo.basic.scheduler.dao.SchedulerJobMapper;
import com.leo.basic.scheduler.quartz.ConcurrentJob;
import com.leo.basic.scheduler.quartz.NonConcurrentJob;
import com.leo.basic.scheduler.service.IOperLogService;
import com.leo.basic.scheduler.service.ISchedulerJobService;
import com.leo.basic.utils.SchedulerUtils;
import com.leo.basic.utils.UUIDCreator;
import com.leo.config.DataSourceConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * @ClassName SchedulerJobServiceImpl
 * @Description 任务操作类
 * @Author wangss
 * @date 2021.09.23 14:42
 * @Version 1.0
 */
@Slf4j
@Service("schedulerJobService")
public class SchedulerJobServiceImpl implements ISchedulerJobService {

    @Autowired
    private IOperLogService operLogService;

    @Autowired
    private Scheduler scheduler;

    @Resource
    private SchedulerJobMapper mapper;

    @Override
    public ResponseData<List<SchedulerJob>> getExecutingJobList() {
        try {
            List<JobExecutionContext> currentlyExecutingJobs = scheduler.getCurrentlyExecutingJobs();
            List<SchedulerJob> jobList = new ArrayList<>(currentlyExecutingJobs.size());
            for (JobExecutionContext context : currentlyExecutingJobs) {
                SchedulerJob job = new SchedulerJob();
                JobDetail jobDetail = context.getJobDetail();
                JobKey key = jobDetail.getKey();
                Trigger trigger = context.getTrigger();
                job.setJobName(key.getName());
                job.setJobGroup(key.getGroup());
                job.setDescription(jobDetail.getDescription());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
                jobList.add(job);
            }
            // 排序
            jobList.sort((Comparator.comparing(SchedulerJob::getJobName)));
            return new ResponseData<>(true, jobList);
        } catch (SchedulerException e) {
            log.error("查询正在运行的定时任务异常：{}", e.getMessage());
            return new ResponseData<>(false, e.getMessage());
        }
    }

    @Override
    public ResponseData createScheduleJob(SchedulerJob schedulerJob) {
        if (schedulerJob == null) {
            return new ResponseData(false, "传入的定时任务信息为空");
        }
        schedulerJob.setJobId(UUIDCreator.getUUID32());
        String jobName = schedulerJob.getJobName();
        String jobGroup = schedulerJob.getJobGroup();
        String jobDescription = schedulerJob.getDescription();
        String cronExpression = schedulerJob.getCronExpression();
        String startDate = schedulerJob.getStartDate();
        String endDate = schedulerJob.getEndDate();
        Date start = new Date();
        Date end = null;

        boolean isSync = false;
        // 并发或者不并发
        Class<? extends Job> jobClass = isSync ? ConcurrentJob.class : NonConcurrentJob.class;
        if (null == this.scheduler) {
            return new ResponseData(false, "scheduler 没有配置");
        }

        boolean isJobExist = false;
        try {
            isJobExist = SchedulerUtils.isJobExists(scheduler, jobName, jobGroup);
        } catch (SchedulerException e) {
            return new ResponseData(false, "检查job是否存在发生异常");
        }

        if (isJobExist) {
            return new ResponseData(false, "任务已存在！");
        }

        if (SchedulerConstant.TRIGGER_TYPE_CRON.equals(schedulerJob.getTriggerType()) && StringUtils.isEmpty(cronExpression)) {
            return new ResponseData(false, "任务计划时间表达式不能为空！");
        }

        List<SchedulerJob> schedulerJobByWhere = mapper.getSchedulerJobByWhere(DataSourceConfig.schedulerJObTableName, schedulerJob);
        if (schedulerJobByWhere != null && schedulerJobByWhere.size() > 0) {
            return new ResponseData(false, "一个任务相同计划执行时间只能添加一次，请核对！");
        }

        // 构建job信息
        JobBuilder jobBuilder = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).withDescription(jobDescription);
        try {
            if (StringUtils.isNotEmpty(startDate)) {
                start = DateUtils.parseDate(startDate, "yyyy-MM-dd");
            }
            if (StringUtils.isNotEmpty(endDate)) {
                end = DateUtils.parseDate(endDate, "yyyy-MM-dd");
            }
            if (SchedulerConstant.TRIGGER_TYPE_REDO.equals(schedulerJob.getTriggerType())) {
                jobBuilder.storeDurably();
                JobDetail build = jobBuilder.build();
                build.getJobDataMap().put(SchedulerConstant.JOB_PARAM_KEY, schedulerJob);
                scheduler.addJob(build, true);
            } else {
                JobDetail build = jobBuilder.build();
                build.getJobDataMap().put(SchedulerConstant.JOB_PARAM_KEY, schedulerJob);
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startAt(start).endAt(end)
                        .withDescription(jobDescription).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
                scheduler.scheduleJob(build, cronTrigger);
            }
        } catch (SchedulerException e) {
            log.error("创建定时任务失败：{}", e.getMessage());
            return new ResponseData(false, "创建定时任务失败，错误信息为：" + e.getMessage());
        } catch (Exception e) {
            log.error("创建定时任务失败：{}", e.getMessage());
            return new ResponseData(false, "创建定时任务失败，错误信息为：" + e.getMessage());
        }
        return new ResponseData(true, "新增定时任务成功！");
    }

    @Override
    public ResponseData storeScheduleJob(SchedulerJob schedulerJob) {
        ResponseData responseData = createScheduleJob(schedulerJob);
        if (responseData.isSuccess()) {
            schedulerJob.setCreateTime(new Date());
            mapper.insertSelective(DataSourceConfig.schedulerJObTableName, schedulerJob);
        }
        return responseData;
    }

    @Override
    public SchedulerJob getJob(String jobId) {
        return mapper.selectByPrimaryKey(DataSourceConfig.schedulerJObTableName, jobId);
    }

    @Override
    public ResponseData delUpdate(SchedulerJob schedulerJob) {
        try {
            String jobId = schedulerJob.getJobId();
            // 先删除
            SchedulerUtils.deleteSchedulerJob(scheduler, schedulerJob.getJobName(), schedulerJob.getJobGroup());
            // 新增计划
            createScheduleJob(schedulerJob);
            schedulerJob.setJobId(jobId);
            // 更新
            schedulerJob.setUpdateTime(new Date());
            mapper.updateByPrimaryKeySelective(DataSourceConfig.schedulerJObTableName, schedulerJob);
        } catch (SchedulerException e) {
            return new ResponseData(false, e.getMessage());
        }
        return new ResponseData(true, "修改数据成功");
    }

    @Override
    public List<SchedulerJob> queryJobList(SchedulerJob schedulerJob) {
        if (null == schedulerJob) {
            schedulerJob = new SchedulerJob();
        }
        List<SchedulerJob> schedulerJobs = mapper.getAllScheduleJob(DataSourceConfig.schedulerJObTableName, schedulerJob);
        if (null == schedulerJobs) {
            return Collections.emptyList();
        }
        try {
            for (SchedulerJob job : schedulerJobs) {
                JobKey jobKey = SchedulerUtils.getJobKey(job.getJobName(), job.getJobGroup());
                List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
                if (CollectionUtils.isEmpty(triggersOfJob)) {
                    continue;
                }
                // 一个任务对应一个触发器
                Trigger next = triggersOfJob.iterator().next();
                Trigger.TriggerState triggerState = scheduler.getTriggerState(next.getKey());
                job.setJobStatus(triggerState.name());

                if (next instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) next;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
            }
        } catch (SchedulerException e) {
            log.error("查询任务异常：{}", e.getMessage());
        }
        return schedulerJobs;
    }

    @Override
    public void toggleJobRun(String jobId, String execType) {
        SchedulerJob schedulerJob = mapper.selectByPrimaryKey(DataSourceConfig.schedulerJObTableName, jobId);
        JobKey jobKey = JobKey.jobKey(schedulerJob.getJobName(), schedulerJob.getJobGroup());
        try {
            if (SchedulerConstant.EXEC_TYPE_PAUSE.equals(execType)) {
                scheduler.pauseJob(jobKey);
            } else if (SchedulerConstant.EXEC_TYPE_RESUEM.equals(execType)) {
                scheduler.resumeJob(jobKey);
            }
        } catch (SchedulerException e) {
            log.error("重启或者恢复定时任务异常：{}", e.getMessage());
        }
    }

    @Override
    public void toggleJobRun(SchedulerJob schedulerJob, String execType) {
        try {
            List<SchedulerJob> list = mapper.getSchedulerJobByWhere(DataSourceConfig.schedulerJObTableName, schedulerJob);
            if (null != list && list.size() > 0) {
                schedulerJob = list.get(0);
            }
            JobKey jobKey = SchedulerUtils.getJobKey(schedulerJob.getJobName(), schedulerJob.getJobGroup());
            if (SchedulerConstant.EXEC_TYPE_PAUSE.equals(execType)) {
                scheduler.pauseJob(jobKey);
            } else if (SchedulerConstant.EXEC_TYPE_RESUEM.equals(execType)) {
                scheduler.resumeJob(jobKey);
            }
        } catch (SchedulerException e) {
            log.error("重启或者恢复定时任务异常：{}", e.getMessage());
        }
    }

    @Override
    public void executeJob(String jobId) {
        SchedulerJob schedulerJob = mapper.selectByPrimaryKey(DataSourceConfig.schedulerJObTableName, jobId);
        try {
            SchedulerUtils.runOnce(scheduler, schedulerJob.getJobName(), schedulerJob.getJobGroup());
        } catch (SchedulerException e) {
            log.error("定时任务 = [{}] 运行失败：{}", schedulerJob.getJobName(), e.getMessage());
        }
    }

    @Override
    public void deleteJob(String jobId) {
        SchedulerJob schedulerJob = mapper.selectByPrimaryKey(DataSourceConfig.schedulerJObTableName, jobId);
        try {
            SchedulerUtils.deleteSchedulerJob(scheduler, schedulerJob.getJobName(), schedulerJob.getJobGroup());
            mapper.deleteByPrimaryKey(DataSourceConfig.schedulerJObTableName, jobId);
        } catch (SchedulerException e) {
            log.error("定时任务 = [{}] 删除失败：{}", schedulerJob.getJobName(), e.getMessage());
        }
    }

    @Override
    public void deleteJob(SchedulerJob schedulerJob) {
        try {
            List<SchedulerJob> list = mapper.getSchedulerJobByWhere(DataSourceConfig.schedulerJObTableName, schedulerJob);
            if (list.size() > 0) {
                // 删除定时任务
                SchedulerUtils.deleteSchedulerJob(scheduler, list.get(0).getJobName(), list.get(0).getJobGroup());
                // 删除数据
                mapper.deleteByPrimaryKey(DataSourceConfig.schedulerJObTableName, list.get(0).getJobId());
            }
        } catch (SchedulerException e) {
            log.error("定时任务 = [{}] 删除失败：{}", schedulerJob.getJobName(), e.getMessage());
        }
    }

    @Override
    public void schedulerSaveLog(SchedulerJob schedulerJob, Date startTime, String exceptionMessage) {
        String userId = "dev";
        String userName = "开发管理员";
        String jobName = schedulerJob.getJobName();
        String trigName = schedulerJob.getJobName();
        String className = schedulerJob.getBeanClass();

        OperLog operLog = new OperLog();
        operLog.setId(UUIDCreator.getUUID32());
        operLog.setUserId(userId);
        operLog.setUserName(userName);
        operLog.setOperType("BATCH_HANDLE");
        operLog.setJobName(jobName);
        operLog.setTriggerName(trigName);
        operLog.setStartTime(startTime);
        operLog.setOperParam("任务名称：" + jobName + ";计划名称：" + trigName + "\n");
        operLog.setBeanClass(className);
        operLog.setMethodName(schedulerJob.getMethodName());
        Date endTime = new Date();
        operLog.setEndTime(endTime);
        operLog.setDuration(new BigDecimal(endTime.getTime() - startTime.getTime()));
        if (StringUtils.isNotEmpty(exceptionMessage)) {
            operLog.setSuccess("1");
            operLog.setOperResult(exceptionMessage);
            schedulerJob.setBatchStatus("0");
        } else {
            operLog.setSuccess("1");
            operLog.setOperResult("任务执行成功！");
            schedulerJob.setBatchStatus("1");
        }
        schedulerJob.setBatchTime(endTime);
        String hostAddress = getHostAddress();
        operLog.setId(hostAddress);
        mapper.updateByPrimaryKeySelective(DataSourceConfig.schedulerJObTableName, schedulerJob);
        operLogService.asyncLog(operLog);
    }

    @Override
    public int beforeTotalLog(String jobName) {
        List<OperLog> operLogListByJobName = operLogService.getOperLogListByJobName(jobName);
        return operLogListByJobName.size();
    }

    private String getHostAddress() {
        String hostAddress = "127.0.0.1";
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("获取ip地址异常：{}", e.getMessage());
        }
        return hostAddress;
    }
}
